#include "Tile.h"
#include "Character.h"
#include "TextLog.h"
#include "Trap.h"
#include "Game.h"
#include "Level.h"
#include "StringLib.h"
#include "Item.h"
#include "Util.h"
#include "Load.h"

Tile::Tile(TileType type, float x, float y, Level* level) {
	m_items = new std::list<Item*>();
	m_character = NULL;
	m_obstacle = NULL;
	m_x = x;
	m_y = y;
	m_type = type;
	m_level = level;
	m_canWalk = true;
	m_explored = m_visible = false;
}

Tile::Tile(float x, float y, Level* level) {
	m_items = new std::list<Item*>();
	m_character = NULL;
	m_obstacle = NULL;
	m_x = x;
	m_y = y;
	m_type = TT_NORMAL;
	m_level = level;
	m_canWalk = true;
	m_explored = m_visible = false;
}

Tile::~Tile() {
	if(m_character) delete m_character;
	if(m_obstacle) delete m_obstacle;
	delete m_items;
}

void Tile::save(std::ofstream& file) const {
	if(m_type == TT_NORMAL && !m_explored && !m_character && m_obstacle != NULL && m_obstacle->getType(TH_GENERIC_TYPE) == GT_OB_WALL && m_items->size() == 0) {
		file << "\n";
	} else {
		file << m_type << " " << (m_canWalk ? 1 : 0) << " " << (m_explored ? 1 : 0) << " ";
		int val = 0;
		if(m_character)
			val |= 1;
		if(m_obstacle)
			val |= 2;
		file << val << " " << m_items->size() << "\n";
		if(m_character)
			m_character->save(file);
		if(m_obstacle)
			m_obstacle->save(file);
		for(std::list<Item*>::const_iterator iter = m_items->begin(); iter != m_items->end(); iter++)
			(*iter)->save(file);
	}
}

void Tile::load(std::ifstream& file) {
	std::string line;
	std::getline(file, line);
	if(line.compare("") == 0) {
		addObstacle(new Wall());
	} else {
		int* tokens = Util::intSplit(line);
		m_type = TileType(tokens[0]);
		m_canWalk = tokens[1] == 1;
		m_explored = tokens[2] == 1;
		if(tokens[3] & 1)
			m_level->addNpc((Npc*)Load::loadCharacter(file), int(m_x), int(m_y));
		if(tokens[3] & 2)
			addObstacle(Load::loadObstacle(this, file));
		for(int i = 0; i < tokens[4]; i++)
			addItem(Load::loadItem(file));
		delete[] tokens;
	}
}

bool Tile::addObstacle(Obstacle* obstacle) {
	if(m_obstacle) return false;
	m_obstacle = obstacle;
	m_obstacle->setTile(this);
	return true;
}

bool Tile::addItem(Item* item) {
	Character::insertItem(m_items, item);
	item->setParentList(m_items);
	return true;
}

bool Tile::addCharacter(Character* character) {
	if(m_character) return false;
	m_character = character;
	m_character->setTile(this);
	return true;
}

bool Tile::removeObstacle(bool destroy) {
	if(!m_obstacle) return false;
	if(destroy)
		delete m_obstacle;
	m_obstacle = NULL;
	return true;
}

bool Tile::removeItem(Item* item, bool destroy) {
	m_items->remove(item);
	if(destroy)
		delete item;
	return true;
}

bool Tile::removeCharacter(bool destroy) {
	if(!m_character) return false;
	if(destroy)
		delete m_character;
	m_character = NULL;
	return true;
}

bool Tile::isEmpty() const {
	return !m_character && !m_obstacle && m_items->empty();
}

bool Tile::isOccupied() const {
	return m_character || (m_obstacle && m_obstacle->isBlocking());
}

Item* Tile::getItem(int i) const {
	std::list<Item*>::const_iterator iter = m_items->begin();
	std::advance(iter, i);
	return *iter;
}

void Tile::logDetails(TextLog& log) const {
	if(m_character && m_character->getType(TH_GENERIC_TYPE) != GT_CH_PLAYER) {
		log.addLine(g_stringLib->getTextString(ST_TILE_SEE_SINGLE, m_character->getColoredName().c_str()));
	} else if(m_obstacle && (m_obstacle->getType(TH_GENERIC_TYPE) != GT_OB_TRAP || ((Trap*)m_obstacle)->isVisible())) {
		log.addLine(g_stringLib->getTextString(ST_TILE_SEE_SINGLE, m_obstacle->getColoredName().c_str()));
	} else if(m_items->size() == 1) {
		log.addLine(g_stringLib->getTextString(ST_TILE_SEE_SINGLE, m_items->front()->getColoredName().c_str()));
	} else if(m_items->size() > 1) {
		log.addLine(g_stringLib->getTextString(ST_TILE_SEE_MULTIPLE));
	}
}

void Tile::setVisible(bool visible) {
	m_visible = visible;
	if(m_visible)
		explore();
}

bool Tile::blocksLOS() const {
	return m_obstacle && (m_obstacle->getType(TH_GENERIC_TYPE) == GT_OB_WALL || (m_obstacle->getType(TH_GENERIC_TYPE) == GT_OB_DOOR && !((Door*)m_obstacle)->isOpen()));
}
